#include "StdAfx.h"
#include "StdAfx.h"
#include "Game.h"

#include "SplashScreen.h"
#include "MainMenu.h"
#include "newGameMenu.h"
#include "gameOverMenu.h"

	char * Game::translatePieceToServer(Piece * piece)
	{
		unsigned char trait = piece->getTrait();
		char result[5];
		result[0] = (trait&0x08 == 0) ? '0':'1';
		result[1] = (trait&0x04 == 0x04) ? '0':'1';
		result[3] = (trait&0x02 == 0x02) ? '0':'1';
		result[2] = (trait&0x01 == 0x01) ? '0':'1';
		result[4] = '\n';
		return result;
	}

	void Game::tournamentStart(char * ipAddress, int portNumber)
	{
		m_pPlayer1 = new AIRandomPlayer;
		RemotePlayer player2;
		sf::IpAddress address("localhost");
		sf::TcpSocket client;
		std::cerr << "creating socket, IP Address: " << address <<", Port Number: " << portNumber << "." << std::endl;
		sf::Socket::Status status = client.connect(address, portNumber);
		if(status == sf::Socket::Done)
		{
			//tournament code starts here
			std::cerr << "connected to socket" << std::endl;

			for(int i = 0; i < m_pieces.size(); ++i) m_pieces[i].setTrait((unsigned char) i);
			m_gameTurn = Player2Piece;
			m_gameState = Playing;
			char * piece = new char[5];
			char move[9];
			char message[100];
			Piece * currentPiece = new Piece;
			std::size_t recieved;
			int turns = 0;
			client.receive(message, 100, recieved);
			std::cerr << "Message from server: " << message << std::endl;
			//while(client.NotReady);
			std::cerr << "sending name to server" << std::endl;
			client.send("marc-valerio\n", 13);
			client.receive(message, 100, recieved);
			std::cerr << "Message from server: " << message << std::endl;
			while(turns < 20)
			{
				switch (m_gameState)
				{
					case Playing:
						switch (m_gameTurn)
						{
							case Player2Piece:
								if(client.receive(piece, 4, recieved) != sf::Socket::Done)
								{
									//exception handling
								}
								client.receive(message, 100, recieved);
								std::cerr << "server says: "<< message << std::endl;
								std::cerr << "the server chose " << piece << std::endl;
								currentPiece = player2.choosePiece(m_pieces, piece);
								std::cerr << "translated: " << (int) currentPiece->getTrait() << std::endl;
								m_gameTurn = Player1Move;
								std::cerr << "player1 moves" << std::endl;
								break;
							case Player1Piece:
								std::cerr << "choosing piece" << std::endl;
								currentPiece = m_pPlayer1->choosePiece(m_mainWindow, m_pieces);
								std::cerr << "piece chosen: " << (int) currentPiece->getTrait() << std::endl;
								piece = translatePieceToServer(currentPiece);
								std::cerr << "translated to: " << piece[0] << piece[1] << piece[2] << piece[3] << ", sending shit to server" << std::endl;
								if(client.send(piece, 5) != sf::Socket::Done)
								{
									//exception handling
								}
								std::cerr << "done, now player 2 moves" << std::endl;
								m_gameTurn = Player2Move;
								break;
							case Player2Move:
								std::cerr << "receiving play from server" << std::endl;
								if(client.receive(move, 9, recieved) != sf::Socket::Done)
								{
									//exception handling
								}
								std::cerr << "server sent: " << move[0] << move[1] << move[2] << move[3] << move[4] << move[5] << move[6] << move[7] << move[8] << std::endl;
								currentPiece = player2.play(currentPiece, m_pieces, move);
								std::cerr << "current piece is: " << (int) currentPiece->getTrait() << std::endl;
								std::cerr << "player1 moves now" << std::endl;
								m_gameTurn = Player1Move;
								break;
							case Player1Move:
								std::cerr << "playing piece: " << (int) currentPiece->getTrait() << std::endl;
								m_pPlayer1->play(m_mainWindow, currentPiece, m_pieces);
								Location newLocation = currentPiece->getLocation();
								std::cerr << "location chosen: " << (int) newLocation.first << ", " << (int) newLocation.second << std::endl;
								char myMove[5];
								myMove[0] = newLocation.first + '0';
								myMove[1] = ',';
								myMove[2] = ' ';
								myMove[3] = newLocation.second + '0';
								myMove[4] = '\n';
								std::cerr << "message sent to server: " << myMove;
								if(client.send(myMove, 5) != sf::Socket::Done)
								{
									//exception handling
								}
								client.receive(message, 100, recieved);
								std::cerr << "server says: " << message << std::endl;
								std::cerr << "player1 chooses" << std::endl;
								m_gameTurn = Player1Piece;
								break;
						}
						break;
					case GameOver:
						turns++;
						for(int i = 0; i < m_pieces.size(); ++i) m_pieces[i].setTrait((unsigned char) i);
						m_gameTurn = Player2Piece;
						m_gameState = Playing;
						//update statistics
						break;
				}
			}
		}
		std::cerr << "could not fucking connect" << std::endl;
		system("PAUSE");
	}

	void Game::start(void)
	{
		if(m_gameState != Game::Uninitialized) return;

		m_mainWindow.create(sf::VideoMode(1024,768,32),"Quarto");
		if(m_boardTexture.loadFromFile("Assets/Images/pEmpty.png"))
		{
			for(int i = 0; i < 4; ++i)
			{
				for(int j = 0; j < 4; ++j)
				{
					m_boardSprites[i][j].setTexture(m_boardTexture);
					m_boardSprites[i][j].setPosition(400+i*56,218+j*76);	
				}
			}
		}else
		{
			//exception handling
		}
		char currentNumber[2];
		for(int i = 0; i < m_pieces.size(); ++i){
			m_pieces[i].setTrait((unsigned char) i); //Initializing traits, from 0x00 (white, small, round, no hole) to 0x0F (black, large, square, hole)
			//Initializing the pieces.... all are out of the board (16,16 is out of the board... read Piece.h comments)
			//std::cerr << "The piece value is " <<   (int *)m_pieces[i].getTrait() << std::endl;
			itoa(i, currentNumber, 10);
			m_piecesVisuals[i].load(std::string("Assets/Images/p") + std::string(currentNumber) + std::string(".png"));
			m_piecesVisuals[i].setPosition(300+(i%8)*53, 612+i/8*78);
		}
		m_gameState = Game::ShowingSplash;

		//i Would add another state to the game (winning state in witch one of the player won the game)
		while(!isExiting())
		{
			gameLoop();
		}

		m_mainWindow.close();
	}

	void Game::updateVisuals(std::vector<PieceVisuals>& pieceVisuals, std::vector<Piece>& pieces)
	{
		for(int i = 0; i < pieceVisuals.size(); ++i)
		{
			std::pair<unsigned char, unsigned char> pos = pieces[i].getLocation();
			if(pos.first != 16)
			{
				if(((int) (pieces[i].getTrait()&0x04)) == 0)pieceVisuals[i].setPosition(400+pos.first*56, 209 + pos.second*76);
				else pieceVisuals[i].setPosition(400+pos.first*56, 194 + pos.second*76);
				if(pos.first < 4 && pos.second < 4)m_boardSprites[pos.first][pos.second].setScale(0,0);
			}
		}
	}

	/*Marc you can check where to put the exit condition for the game loop (winning or no pieces left)
	*/
	void Game::gameLoop(void)
	{
		sf::Event currentEvent;
		//can be initialized to Player1Piece or Player2Piece
		//m_gameTurn = Player1Piece;
		while(m_mainWindow.pollEvent(currentEvent) /*&& (m_winner==0) && m_piecesLeft*/)
		{
			switch(m_gameState)
			{
				case Game::Playing:
					m_mainWindow.clear(sf::Color(0,0,0));
					updateVisuals(m_piecesVisuals, m_pieces);
					for(int i = 0; i < 16; ++i)
					{
						m_piecesVisuals[i].draw(m_mainWindow);
						m_mainWindow.draw(m_boardSprites[i%4][i/4]);
					}

					m_mainWindow.display();
					//added the game loop here more or less
					switch(m_gameTurn)
					{
						case Game::Player1Piece:
							m_currentPiece = m_pPlayer1->choosePiece(m_mainWindow, m_pieces);
							m_gameTurn = Game::Player2Move;
							break;
						case Game::Player2Piece:
							m_currentPiece = m_pPlayer2->choosePiece(m_mainWindow, m_pieces);
							m_gameTurn = Player1Move;
							break;
						case Game::Player1Move:
							m_pPlayer1->play(m_mainWindow, m_currentPiece, m_pieces);
							m_winner = (Game::isInFinalState()) ? 1 : 0;
							m_piecesLeft = Game::thereArePiecesLeft();
							m_gameTurn = Player1Piece;
							break;
						case Game::Player2Move:
							m_pPlayer2->play(m_mainWindow, m_currentPiece, m_pieces);
							m_winner = (Game::isInFinalState()) ? 2 : 0;
							m_piecesLeft = Game::thereArePiecesLeft();							
							m_gameTurn = Player2Piece;
							break;
					}
					if(currentEvent.type == sf::Event::Closed) m_gameState = Game::Exiting;
					else if(m_winner || !m_piecesLeft) m_gameState = Game::GameOver;
					break;

				case Game::ShowingSplash:
					showSplashScreen();
					break;

				case Game::ShowingMenu:
					showMenu();
					break;

				case Game::NewGameMenu:
					showNewGameMenu();
					break;

				case Game::GameOver:
					showGameOverMenu(m_winner);
					break;
			}
		}
	}

	//check if there are pieces left to use
	bool Game::thereArePiecesLeft(std::vector<Piece> my_pieces)
	{
		for(int i =0; i< my_pieces.size(); i++)
		{
			//location must be initialized to 16,16 for ALL for it to work
			if(my_pieces[i].getLocation().first==16 && my_pieces[i].getLocation().second==16)
				return true;
		}
		return false;
	}

	//check if there are pieces left to use
	bool Game::thereArePiecesLeft(void)
	{
		for(int i =0; i< m_pieces.size(); i++)
		{
			//location must be initialized to 16,16 for ALL for it to work
			if(m_pieces[i].getLocation().first==16 && m_pieces[i].getLocation().second==16)
				return true;
		}
		return false;
	}


	/*check if the current state is a winning state
	it's really ugly like this... maybe later we can make it better*/
	bool Game::isInFinalState(void)
	{
		//debugging
		//for(int i = 0; i<m_pieces.size(); i++)
		//	std::cerr << "The piece trait is " <<  (int *)m_pieces[i].getTrait() << std::endl;
		bool partial1, partial2, partial3, partial4;
		Location l1,l2,l3,l4;
		partial1 = partial2 = partial3 = partial4 = false;
		//check the lines
		for(int i = 0; i<4; i++)
		{
			l1 = std::make_pair (i,0);
			l2 = std::make_pair (i,1);
			l3 = std::make_pair (i,2);
			l4 = std::make_pair (i,3);
			if(Game::thereIsPiece(l1) &&
				Game::thereIsPiece(l2) &&
				Game::thereIsPiece(l3) &&
				Game::thereIsPiece(l4))
			{
				partial1 = partial1 || Game::hasCommonTraits(l1,l2,l3,l4);
			}
		}

		//check the columns
		for(int i= 0; i<4; i++)
		{
			l1 = std::make_pair (0, i);
			l2 = std::make_pair (1, i);
			l3 = std::make_pair (2, i);
			l4 = std::make_pair (3, i);
			if(Game::thereIsPiece(l1) &&
				Game::thereIsPiece(l2) &&
				Game::thereIsPiece(l3) &&
				Game::thereIsPiece(l4))
			{
				partial2 = partial2 || Game::hasCommonTraits(l1,l2,l3,l4);
			}
		}

		//check first diagonal
		l1 = std::make_pair (0, 0);
		l2 = std::make_pair (1, 1);
		l3 = std::make_pair (2, 2);
		l4 = std::make_pair (3, 3);

		if(Game::thereIsPiece(l1) &&
				Game::thereIsPiece(l2) &&
				Game::thereIsPiece(l3) &&
				Game::thereIsPiece(l4))
		{
			partial3 = Game::hasCommonTraits(l1,l2,l3,l4);
		}

		//second diagonal
		l1 = std::make_pair (0, 3);
		l2 = std::make_pair (1, 2);
		l3 = std::make_pair (2, 1);
		l4 = std::make_pair (3, 0);

		if(Game::thereIsPiece(l1) &&
				Game::thereIsPiece(l2) &&
				Game::thereIsPiece(l3) &&
				Game::thereIsPiece(l4))
		{
			partial4 = Game::hasCommonTraits(l1,l2,l3,l4);
		}

		return (partial1 || partial2 || partial3 || partial4);
	}

	/*check if a certain state is final*/
	bool Game::isInFinalState(std::vector<Piece> my_pieces)
	{
		//debugging
		//for(int i = 0; i<m_pieces.size(); i++)
		//	std::cerr << "The piece trait is " <<  (int *)m_pieces[i].getTrait() << std::endl;
		bool partial1, partial2, partial3, partial4;
		Location l1,l2,l3,l4;
		partial1 = partial2 = partial3 = partial4 = false;
		//check the lines
		for(int i = 0; i<4; i++)
		{
			l1 = std::make_pair (i,0);
			l2 = std::make_pair (i,1);
			l3 = std::make_pair (i,2);
			l4 = std::make_pair (i,3);
			if(Game::thereIsPiece(l1, my_pieces) &&
				Game::thereIsPiece(l2, my_pieces) &&
				Game::thereIsPiece(l3, my_pieces) &&
				Game::thereIsPiece(l4, my_pieces))
			{
				partial1 = partial1 || Game::hasCommonTraits(l1,l2,l3,l4, my_pieces);
			}
		}

		//check the columns
		for(int i= 0; i<4; i++)
		{
			l1 = std::make_pair (0, i);
			l2 = std::make_pair (1, i);
			l3 = std::make_pair (2, i);
			l4 = std::make_pair (3, i);
			if(Game::thereIsPiece(l1, my_pieces) &&
				Game::thereIsPiece(l2, my_pieces) &&
				Game::thereIsPiece(l3, my_pieces) &&
				Game::thereIsPiece(l4, my_pieces))
			{
				partial2 = partial2 || Game::hasCommonTraits(l1,l2,l3,l4, my_pieces);
			}
		}

		//check first diagonal
		l1 = std::make_pair (0, 0);
		l2 = std::make_pair (1, 1);
		l3 = std::make_pair (2, 2);
		l4 = std::make_pair (3, 3);

		if(Game::thereIsPiece(l1) &&
				Game::thereIsPiece(l2, my_pieces) &&
				Game::thereIsPiece(l3, my_pieces) &&
				Game::thereIsPiece(l4, my_pieces))
		{
			partial3 = Game::hasCommonTraits(l1,l2,l3,l4, my_pieces);
		}

		//second diagonal
		l1 = std::make_pair (0, 3);
		l2 = std::make_pair (1, 2);
		l3 = std::make_pair (2, 1);
		l4 = std::make_pair (3, 0);

		if(Game::thereIsPiece(l1, my_pieces) &&
				Game::thereIsPiece(l2, my_pieces) &&
				Game::thereIsPiece(l3, my_pieces) &&
				Game::thereIsPiece(l4, my_pieces))
		{
			partial4 = Game::hasCommonTraits(l1,l2,l3,l4, my_pieces);
		}

		return (partial1 || partial2 || partial3 || partial4);
	}

	//check if in a specific location there is a piece in my_pieces
	bool Game::thereIsPiece(Location location, std::vector<Piece> my_pieces)
	{
		for(int i=0; i<my_pieces.size(); i++)
		{
			if(my_pieces[i].getLocation().first==location.first && my_pieces[i].getLocation().second==location.second)
				return true;
		}
		return false;
	}

	//check if in a specific location there is a piece
	bool Game::thereIsPiece(Location location)
	{
		for(int i=0; i<m_pieces.size(); i++)
		{
			if(m_pieces[i].getLocation().first==location.first && m_pieces[i].getLocation().second==location.second)
				return true;
		}
		return false;
	}

	//check for common traits in my_pieces
	bool Game::hasCommonTraits(Location l1, Location l2, Location l3, Location l4, std::vector<Piece> my_pieces)
	{
		Location loc;		
		Piece p1,p2,p3,p4; //I first find the pieces in the location
		for(int i = 0; i<my_pieces.size(); i++)
		{
			loc = my_pieces[i].getLocation();
			if((loc).first == l1.first && (loc).second == l1.second)
				p1 = my_pieces[i];
			else if((loc).first == l2.first && (loc).second == l2.second)
				p2 = my_pieces[i];
				//std::cerr << "The piece is " <<   (int *)(*p2).getTrait() << std::endl;
			else if((loc).first == l3.first && (loc).second == l3.second)
				p3 = my_pieces[i];
				//std::cerr << "The piece is " <<   (int *)(*p3).getTrait() << std::endl;
			else if((loc).first == l4.first && (loc).second == l4.second)
				p4 = my_pieces[i];
				//std::cerr << "The piece is " <<   (int *)(*p4).getTrait() << std::endl;
		}
		//perform an XNOR on the traits... if the result is different from 0x00 we have a match
		unsigned char result = ((p1).getTrait()&(p2).getTrait() & (p3).getTrait()&(p4).getTrait())|(~(p1).getTrait()&~(p2).getTrait() & ~(p3).getTrait()&~(p4).getTrait()) << (4);
		return (result!=0); 
		//return (~(~((*p1).getTrait()^(*p2).getTrait())^(~((*p3).getTrait()^(*p4).getTrait())))!='0x00') ? true : false;
	}

	//check for common traits
	bool Game::hasCommonTraits(Location l1, Location l2, Location l3, Location l4)
	{
		Location loc;		
		Piece p1,p2,p3,p4; //I first find the pieces in the location
		for(int i = 0; i<m_pieces.size(); i++)
		{
			loc = m_pieces[i].getLocation();
			if((loc).first == l1.first && (loc).second == l1.second)
				p1 = m_pieces[i];
			else if((loc).first == l2.first && (loc).second == l2.second)
				p2 = m_pieces[i];
				//std::cerr << "The piece is " <<   (int *)(*p2).getTrait() << std::endl;
			else if((loc).first == l3.first && (loc).second == l3.second)
				p3 = m_pieces[i];
				//std::cerr << "The piece is " <<   (int *)(*p3).getTrait() << std::endl;
			else if((loc).first == l4.first && (loc).second == l4.second)
				p4 = m_pieces[i];
				//std::cerr << "The piece is " <<   (int *)(*p4).getTrait() << std::endl;
		}
		//perform an XNOR on the traits... if the result is different from 0x00 we have a match
		unsigned char result = ((p1).getTrait()&(p2).getTrait() & (p3).getTrait()&(p4).getTrait())|(~(p1).getTrait()&~(p2).getTrait() & ~(p3).getTrait()&~(p4).getTrait()) << (4);
		return (result!=0); 
		//return (~(~((*p1).getTrait()^(*p2).getTrait())^(~((*p3).getTrait()^(*p4).getTrait())))!='0x00') ? true : false;
	}

	void Game::showSplashScreen(void)
	{
		SplashScreen splashScreen;
		splashScreen.show(m_mainWindow);
		m_gameState = Game::ShowingMenu;
	}

	void Game::showMenu(void)
	{
		MainMenu mainMenu;
		switch (mainMenu.show(m_mainWindow))
		{
			case MainMenu::NewGame:
				m_gameState = Game::NewGameMenu;
				break;
			case MainMenu::Exit:
				m_gameState = Game::Exiting;
				break;
		}
	}

	void Game::showGameOverMenu(unsigned char winner)
	{
		gameOverMenu mainMenu;
		switch (mainMenu.show(m_mainWindow, (int) winner))
		{
			case MainMenu::NewGame:
				m_winner = 0;
				m_gameState = Game::NewGameMenu;
				break;
			case MainMenu::Exit:
				m_gameState = Game::Exiting;
				break;
		}
	}

	void Game::showNewGameMenu(void)
	{
		srand((unsigned)time(0));
		newGameMenu mainMenu;
		switch (mainMenu.show(m_mainWindow))
		{
			case newGameMenu::AIvsAI:
				m_gameState = Game::Playing;
				/*m_pPlayer1 = new AIPlayer;
				m_gameTurn = Game::Player1Piece;*/
				//m_pPlayer1 = new AIPlayer(4); //so it compiles... should be selectable
				//m_pPlayer2 = new AIPlayer(4);
				m_pPlayer1 = new AIRandomPlayer;
				m_pPlayer2 = new AIRandomPlayer;
				m_gameTurn = (rand()%2) ? Game::Player1Piece : Game::Player2Piece;
				m_whoStarted = m_gameTurn;				break;
			case newGameMenu::LocalvsAI:
				m_gameState = Game::Playing;
				m_pPlayer1 = new LocalPlayer;				
				m_pPlayer2 = new AIPlayer(4);
				//m_pPlayer2 = new AIRandomPlayer;
				m_gameTurn = (rand()%2) ? Game::Player1Piece : Game::Player2Piece;
				m_whoStarted = m_gameTurn;
				break;
			case newGameMenu::LocalvsLocal:
				m_gameState = Game::Playing;
				m_pPlayer1 = new LocalPlayer;				
				m_pPlayer2 = new LocalPlayer;
				m_gameTurn = (rand()%2) ? Game::Player1Piece : Game::Player2Piece;
				m_whoStarted = m_gameTurn;
				break;
			case newGameMenu::LocalvsRemote:
				m_gameState = Game::Playing;
				m_pPlayer1 = new LocalPlayer;				
				m_pPlayer2 = new RemotePlayer;
				m_gameTurn = (rand()%2) ? Game::Player1Piece : Game::Player2Piece;
				m_whoStarted = m_gameTurn;
				break;
			case newGameMenu::AIvsRemote:
				m_gameState = Game::Playing;
				//m_pPlayer1 = new AIPlayer(4); //same as b4
				m_pPlayer1 = new AIRandomPlayer;
				m_pPlayer2 = new RemotePlayer;
				m_gameTurn = (rand()%2) ? Game::Player1Piece : Game::Player2Piece;
				m_whoStarted = m_gameTurn;
				break;
			case newGameMenu::RemotevsRemote:
				m_gameState = Game::Playing;
				m_pPlayer1 = new RemotePlayer;				
				m_pPlayer2 = new RemotePlayer;
				m_gameTurn = (rand()%2) ? Game::Player1Piece : Game::Player2Piece;
				m_whoStarted = m_gameTurn;
				break;
		}
	}
	
	Game::State Game::m_gameState = Uninitialized;
	Game::Turn Game::m_gameTurn = Error;
	Game::Type Game::m_gameType = TypeError;
	sf::RenderWindow Game::m_mainWindow;
	Player * Game::m_pPlayer1;
	Player * Game::m_pPlayer2;
	std::vector<Piece> Game::m_pieces(16);
	//keep the state of the game
	unsigned char Game::m_winner=0;
	bool Game::m_piecesLeft = true;
	Piece * Game::m_currentPiece = 0;
	sf::Texture Game::m_boardTexture;
	sf::Sprite Game::m_boardSprites[4][4];
	std::vector<Game::PieceVisuals> Game::m_piecesVisuals(16); 
	Game::Turn Game::m_whoStarted;